查看原文
其他

Rust学习资料

程序喵大人 程序喵大人 2023-02-24

最近在研究Rust,目前大多数项目都可以使用Rust开发,但是涉及到和其他语言交互,比如用Rust开发一个SDK,一般还是需要导出C接口。


那如何将Rust导出C接口?

Rust的FFI就是专门做这件事的。一个正常的Rust public接口长这样:

pub fn hello_world() -> i32 { 20}

如果要把一个Rust函数导出为C接口,需要对它进行改造:

#[no_mangle]pub extern "C" fn hello_world() -> i32 { 20}

它相比于纯Rust函数有两点不同:

一个是extern "C":表示导出C接口

一个是#[no_mangle]:正常一个C++或者Rust函数相关的符号都特别长且难以理解,使用它表示导出的符号是C符号,即没有任何多余的修饰,函数名是啥样,相关的符号大概就是啥样,如图:

如何导出C的动态库或者静态库?

如果想要编译出动态库或者静态库,可以在Cargo.toml中配置crate-type:

[lib]crate-type = ["cdylib"] # Creates dynamic lib# crate-type = ["staticlib"] # Creates static lib

cylib表示导出动态库,staticlib表示导出静态库。


如何生成对应的C头文件?

一个C库一般都有个头文件,那Rust怎么生成一个C的头文件呢?可以使用cbindgen:

cbindgen --config cbindgen.toml --crate hello_world --output hello_world.h

其中cbindgen.toml是一个template文件,我后面链接中列了具体地址。


上面的hello_world函数,我使用cbindgen就可以自动生成一个头文件:

#pragma once
#include <stdarg.h>#include <stdbool.h>#include <stddef.h>#include <stdint.h>#include <stdlib.h>
#ifdef __cplusplusextern "C" {#endif // __cplusplus
int32_t hello_world(void);
#ifdef __cplusplus} // extern "C"#endif // __cplusplus

至于如何使用更复杂的类型和C交互,比如我想导出和传入一个结构体的指针,比如我想设置const char*,以及怎么管理对应的内存?


可以直接看这段代码:

use std::boxed::Box;use std::ffi::CStr;use std::ffi::CString;use std::os::raw::c_char;
pub struct Manager { path: CString,}
fn get_default_cstring() -> CString { CString::new("").expect("new string failed")}
#[no_mangle]pub extern "C" fn manager_create() -> *mut Manager { println!("{}", "create_manager().".to_string()); Box::into_raw(Box::new(Manager { path: get_default_cstring(), }))}
#[no_mangle]pub extern "C" fn manager_destroy(ptr: *mut Manager) { if ptr.is_null() { return; }
// safe unsafe { let _b = Box::from_raw(ptr); }}
impl Manager { #[no_mangle] pub extern "C" fn manager_set_path(&mut self, p: *const c_char) { unsafe { self.path = CString::from(CStr::from_ptr(p)); } }
#[no_mangle] pub extern "C" fn manager_get_function(&self) -> *const c_char { self.path.as_ptr() }}
#[no_mangle]pub extern "C" fn hello_world() -> i32 { 20}

也许有人不太理解Rust代码的含义,那可以直接看它对应的C Header:

#pragma once
#include <stdarg.h>#include <stdbool.h>#include <stddef.h>#include <stdint.h>#include <stdlib.h>
typedef struct Manager Manager;
#ifdef __cplusplusextern "C" {#endif // __cplusplus
int32_t hello_world(void);
struct Manager *manager_create(void);
void manager_destroy(struct Manager *ptr);
const char *manager_get_function(const struct Manager *self);
void manager_set_path(struct Manager *self, const char *p);
#ifdef __cplusplus} // extern "C"#endif // __cplusplus

通过manager_create创建的内存,需要通过manager_destroy销毁。


但是在对应的Rust代码没有用到申请或者销毁内存相关的代码,而是使用Box来管理内存,它可以理解为C++中的unique_ptr。


内存都通过对象来管理,避免Rust申请的内存,让C这边来释放,违反代码的开发准则。


当然,如果你非要在Rust层想malloc和free内存,可以使用libc的crate。


如果想在Rust中调用C或者C++代码,可以使用cxx crate,也很方便,比如Rust中的String、Vec都在cxx中有对应的类型。


但是我的目的是使用Rust开发一个C的动态库的SDK,两种方法都尝试了下,感觉还是直接使用FFI更方便些。


我这里特意整理了一些Rust FFI相关资料,感兴趣的可以看看

  • Rust如何调用C接口、Rust如何导出C接口、C的callback如何传给Rust:https://doc.rust-lang.org/nomicon/ffi.html

  • The Embedded Rust Book FFI:https://docs.rust-embedded.org/book/interoperability/rust-with-c.html

  • 使用cbindgen可以自动分析,将Rust接口导出为C接口:https://github.com/eqrion/cbindgen

  • cbindgen的default template:https://github.com/eqrion/cbindgen/blob/master/template.toml

  • cbindgen的doc:https://github.com/eqrion/cbindgen/blob/master/docs.md

  • Rust FFI的 example blog,主要是string如何传出去并销毁相关内存:https://snacky.blog/en/string-ffi-rust.html

  • cxx专用于Rust和C++之间的桥梁:https://github.com/dtolnay/cxx

  • cxx:https://cxx.rs/

  • Complex data types and Rust FFI blog:http://kmdouglass.github.io/posts/complex-data-types-and-the-rust-ffi/

  • Rust std ffi:https://doc.rust-lang.org/std/ffi/index.html

  • 与C交互时,可以使用libc在Rust层做C的malloc和free

  • 涉及ptr的地方需要了解:https://doc.rust-lang.org/std/ptr/index.html


还有些Rust入门资料

  • https://www.zhihu.com/question/31038569 

  • https://doc.rust-lang.org/rust-by-example/ 

  • https://doc.rust-lang.org/cargo/getting-started/installation.html 

  • https://github.com/rustlang-cn/Rustt 

  • https://github.com/sunface/rust-course 


更多内容在 一个优质的C++学习圈 里,来一起钻研C++和Rust吧。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存